#include "rfid.h"

Rfid::Rfid(QObject *parent) : QObject(parent)
{
    timer_refresh = 1000; //ms
    timer = new QTimer;
    timer->setInterval(timer_refresh);
    connect(timer, &QTimer::timeout, this, &Rfid::timeout);
    qDebug() << "Rfid new done.";
}

void Rfid::startListen()
{
    qDebug() << "Rfid class startListen";
    timer->start();
}

void Rfid::stopListen()
{
    qDebug() << "Rfid class stopListen";
    timer->stop();
}

void Rfid::timeout()
{
//    qDebug() << "Rfid timer timeout";
    readCardId();
}

/*
UART 接口一帧的数据格式为 1 个起始位， 8 个数据位、无奇偶校验位、 1 个停止位，波
特率固定为 9600。
*/
int Rfid::init_tty(int fd)
{
   struct termios old_cfg,new_cfg;

   //1.得到当前串口的属性配置 int tcgetattr (int __fd, struct termios *__termios_p)
   if(tcgetattr(fd,&old_cfg) != 0)
   {

       perror("tcgetattr");
       return -1;
   }

   //2.设置串口的通信模式 void cfmakeraw (struct termios *__termios_p)
   //将新的配置属性清零
   bzero(&new_cfg,sizeof(new_cfg));

   new_cfg = old_cfg;
   cfmakeraw(&new_cfg);
   //3 配置串口的波特率 数据位 停止位  奇偶校验位
   //int cfsetispeed (struct termios *__termios_p, speed_t __speed)
   cfsetispeed(&new_cfg,B9600);
   cfsetospeed(&new_cfg,B9600);

   //激活选项：CLOCAL 和 CREAD 分别用于本地连接和接受使能
   new_cfg.c_cflag |=  CLOCAL | CREAD;

   //设置数据位:8位，需使用掩码设置
   new_cfg.c_cflag &= ~CSIZE;
   new_cfg.c_cflag |= CS8;
   //设置无校验
   new_cfg.c_cflag &= ~PARENB;
   //设置停止位：1个bit
    new_cfg.c_cflag &= ~CSTOPB;

    //4 清空缓冲区
     new_cfg.c_cc[VTIME]  = 0;
   new_cfg.c_cc[VMIN] = 4;
   tcflush(fd,TCIFLUSH);

   //5 将配置好的串口给设置到系统
   if(tcsetattr(fd,TCSANOW,&new_cfg) != 0)
   {
       perror("tcsetattr");
       return -1;
   }

   return 0;

}

//定义校验和
unsigned char Rfid::calc_BCC(unsigned char*SerBfr,int/* len*/)
{
   unsigned char BCC = 0;
   int i = 0;
   for(; i<(SerBfr[0]-2); i++) {
        BCC ^= *(SerBfr + i);
   }
   return ~BCC;
}
//请求命令--A
int Rfid::piccRequest(int fd)
{
   //[1] 定义数据格式
   unsigned char wbuf[128],rbuf[128];
   fd_set rfds;
  struct timeval tv;
   int retval;

   memset(wbuf,0,128);
   memset(rbuf,0,128);

   //[2] 主机向从机发起请求
   wbuf[0] = 0x07; //数据帧长度:0x07
   wbuf[1] = 0x02;//命令类型：ISO14443A类型
   wbuf[2] = 0x41;//具体的命令'A' --- 65
   wbuf[3] = 0x01;//信息的长度:1B
   wbuf[4] = 0x52;//具体的命令
   wbuf[5] = calc_BCC(wbuf,wbuf[0]-2);//校验和
   wbuf[6] = 0x03;//结束符
   //[3] 把数据写入到串口中
   write(fd,wbuf,7);

   //[4] 等待下位机的模块的响就 --- io复用来监听select
    /* Watch 串口 (fd ) to see when it has input. */
  FD_ZERO(&rfds);
  FD_SET(fd, &rfds);
   /* Wait up to 300 seconds. */
//  tv.tv_sec = 300;
  tv.tv_sec = 0;
  tv.tv_usec = 0;

  //开启监听
    qDebug() << "helloworld";
    //ffmpeg -i 晚安房间壁纸.mp4 -strict -2 -vf scale=1920:1080 晚安房间壁纸1.mp4
  retval = select(fd + 1, &rfds, NULL, NULL, &tv);

  switch(retval)
  {
      case -1:
      perror("piccRequest");
      break;
      case 0:
      printf("timeout\n");
      break;
      default:
       //监听成功，可以读取数据

       retval = read(fd,rbuf,8);
       if(retval < 0)
       {
           printf("piccRequest = %d\n",retval);
       }

       if(rbuf[2] == 0x00)
       {
           printf("piccRequest sucess\n");
           return 0;
       }

  }

  FD_CLR(fd, &rfds);

   return 1;
}

//防碰撞
int Rfid::piccAnticoll(int fd)
{
   //[1] 定义数据格式
   unsigned char wbuf[128],rbuf[128];
   fd_set rfds;
  struct timeval tv;
   int retval;
  unsigned int ID;
   memset(wbuf,0,128);
   memset(rbuf,0,128);

   //[2] 主机向从机发起请求
   wbuf[0] = 0x08; //数据帧长度:0x08
   wbuf[1] = 0x02;//命令类型：ISO14443A类型
   wbuf[2] = 0x42;//具体的命令'B' --- 66
   wbuf[3] = 0x02;//信息的长度:1B
   wbuf[4] = 0x93;//具体的数据
   wbuf[5] = 0x00;
   wbuf[6] = calc_BCC(wbuf,wbuf[0]-2);//校验和
   wbuf[7] = 0x03;//结束符
   //[3] 把数据写入到串口中
   write(fd,wbuf,8);
    /* Watch 串口 (fd ) to see when it has input. */
  FD_ZERO(&rfds);
  FD_SET(fd, &rfds);
   /* Wait up to 300 seconds. */
//  tv.tv_sec = 300;
  tv.tv_sec = 0;
  tv.tv_usec = 0;

  //开启监听
  retval = select(fd + 1, &rfds, NULL, NULL, &tv);

  switch(retval)
  {

      case -1:
      perror("piccAnticoll");
      break;
      case 0:
      printf("timeout\n");
      break;
      default:
       //监听成功，可以读取数据
       retval = read(fd,rbuf,10);
       if(retval < 0)
       {
           printf("piccAnticoll = %d\n",retval);
       }

       if(rbuf[2] == 0x00)
       {
//           printf("piccAnticoll sucess\n");
           //ID是存放在rbuf中4 5 6 7个元素中的内容
           ID = rbuf[7]<<24 | rbuf[6]<<16 | rbuf[5]<<8 | rbuf[4];

//           printf("piccAnticoll the card id = %x\n",ID);
           QString id = QString(ID);
           emit readID(id); // 发送读写信号
           qDebug() << "emit readID signal.";
           return 0;
       }

  }

  FD_CLR(fd, &rfds);
   return 1;
}

//读取电子标签的ID
void Rfid::readCardId(void)
{
   //[1] 打开串口

   int fd = open(USART1,O_RDWR);
   if(fd == -1)
   {
       perror("串口打开失败");
       return ;
   }

   //[2] 初始化串口
   init_tty(fd);

   //[3]根据开发文档中的访问设备的流程 ---请求(A) --->防碰撞(B)
   if(piccRequest(fd))
   {
//       printf("piccRequest falied\n");
       return;
   }


   if(piccAnticoll(fd))
   {
       printf("piccAnticoll failed\n");
       return;
   }

}
